﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sherwood.SignOn.Server;
using Sherwood.SignOn.Server.Models;
using Sherwood.SignOn.Server.Models.ViewModels;
using Sherwood.SignOn.Server.Controllers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Web.Mvc;
using Sherwood.SignOn.Server.Models.Repositories;
using Sherwood.Localization;
using Sherwood.SignOn.Server.Models.Services;


namespace Sherwood.SignOn.Server.Tests
{
    [TestClass]
    public class ServicesTests : TestBase
    {
        /// <summary>
        /// Tests client authentication (should fail if client doesn't exist)
        /// </summary>
        [TestMethod]
        public void Authenticate_Bad_Client_Code_Test()
        {
            //Assemble            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");
                        

            //Act
            AuthenticateServiceViewModel viewModel = new AuthenticateServiceViewModel();
            viewModel.ClientCode = "fakeClient";
            viewModel.UserName = userAccount.UserName; //genuine username
            viewModel.Password = "password"; //genuine password
            viewModel.Timestamp = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            viewModel.Signature = TestHelper.GetSignature(viewModel.UserName + "|" + viewModel.Password + "|" + viewModel.ClientCode + "|" + viewModel.Timestamp, "ClientAPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.Authenticate(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(string.IsNullOrEmpty(result.Content) || result.Content.StartsWith("<serviceException"),"Content should be empty, because this client doesn't exist.");
        }


        /// <summary>
        /// Tests client signature check (should fail if signature is wrong).
        /// </summary>
        [TestMethod]
        public void Authenticate_Bad_Client_Signature_Test()
        {
            //Assemble
            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");


            //Act
            AuthenticateServiceViewModel viewModel = new AuthenticateServiceViewModel();
            viewModel.ClientCode = "ClientA"; //genuine client
            viewModel.UserName = userAccount.UserName; //genuine username
            viewModel.Password = "password"; //genuine password
            viewModel.Timestamp = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            //wrong signature
            viewModel.Signature = TestHelper.GetSignature(viewModel.UserName + "|" + viewModel.Password + "|" + viewModel.ClientCode + "|" + viewModel.Timestamp, "ClientBPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.Authenticate(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(string.IsNullOrEmpty(result.Content) || result.Content.StartsWith("<serviceException"), "Content should be empty, because message is signed with wrong key.");
        }


        [TestMethod]
        public void Authenticate_Invalid_Password_Test()
        {
            //Assemble
            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");


            //Act
            AuthenticateServiceViewModel viewModel = new AuthenticateServiceViewModel();
            viewModel.ClientCode = "ClientA"; //genuine client
            viewModel.UserName = userAccount.UserName; //genuine username
            viewModel.Password = "password1234"; //invalid password
            viewModel.Timestamp = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            viewModel.Signature = TestHelper.GetSignature(viewModel.UserName + "|" + viewModel.Password + "|" + viewModel.ClientCode + "|" + viewModel.Timestamp, "ClientAPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.Authenticate(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(string.IsNullOrEmpty(result.Content) || result.Content.StartsWith("<serviceException"), "Content should be empty, because password is invalid.");
        }

        [TestMethod]
        public void Authenticate_Invalid_Username_Test()
        {
            //Assemble
            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");


            //Act
            AuthenticateServiceViewModel viewModel = new AuthenticateServiceViewModel();
            viewModel.ClientCode = "ClientA"; //genuine client
            viewModel.UserName = "fakeUser"; //invalid username
            viewModel.Password = "password"; //genuine password
            viewModel.Timestamp = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            viewModel.Signature = TestHelper.GetSignature(viewModel.UserName + "|" + viewModel.Password + "|" + viewModel.ClientCode + "|" + viewModel.Timestamp, "ClientAPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.Authenticate(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(string.IsNullOrEmpty(result.Content) || result.Content.StartsWith("<serviceException"), "Content should be empty, because password is invalid.");
        }

        /// <summary>
        /// Tests that requests with an old timestamp (10 minutes old) return an empty value
        /// </summary>
        [TestMethod]
        public void Authenticate_Old_Date_Test()
        {
            //Assemble
            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");

            //Act
            AuthenticateServiceViewModel viewModel = new AuthenticateServiceViewModel();
            viewModel.ClientCode = "ClientA"; //genuine client
            viewModel.UserName = "username"; //genuine username
            viewModel.Password = "password"; //genuine password
            viewModel.Timestamp = DateTime.Now.AddSeconds((int)Config.Settings.RequestValidityInSeconds+60).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); //old timestamp
            viewModel.Signature = TestHelper.GetSignature(viewModel.ClientCode + "|" + viewModel.Timestamp, "ClientAPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.Authenticate(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(string.IsNullOrEmpty(result.Content) || result.Content.StartsWith("<serviceException"), "Content should be empty, because timestamp is outside of allowed range.");
        }


        [TestMethod]
        public void Authenticate_Valid_Test()
        {
            //Assemble
            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");

            //Act
            AuthenticateServiceViewModel viewModel = new AuthenticateServiceViewModel();
            viewModel.ClientCode = "ClientA"; //genuine client
            viewModel.UserName = "username"; //genuine username
            viewModel.Password = "password"; //genuine password
            viewModel.Timestamp = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            viewModel.Signature = TestHelper.GetSignature(viewModel.SignatureContent, "ClientAPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.Authenticate(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(!string.IsNullOrEmpty(result.Content), "Content should not be empty!");
        }


        [TestMethod]
        public void GetUserProfile_With_Email_Test()
        {
            //Assemble
            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");

            //Act
            GetUserProfileServiceViewModel viewModel = new GetUserProfileServiceViewModel();
            viewModel.ClientCode = "ClientA"; //genuine client
            viewModel.UserName = null; //no username
            viewModel.Email = userAccount.Email; //genuine email
            viewModel.Timestamp = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            viewModel.Signature = TestHelper.GetSignature(viewModel.SignatureContent, "ClientAPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.GetUserProfile(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(!string.IsNullOrEmpty(result.Content), "Content should be empty, since a valid email address was provided.");
        }

        [TestMethod]
        public void GetUserProfile_With_Username_Test()
        {
            //Assemble
            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");

            //Act
            GetUserProfileServiceViewModel viewModel = new GetUserProfileServiceViewModel();
            viewModel.ClientCode = "ClientA"; //genuine client
            viewModel.UserName = "username"; //genuine username
            viewModel.Email = null; //no email
            viewModel.Timestamp = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            viewModel.Signature = TestHelper.GetSignature(viewModel.SignatureContent, "ClientAPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.GetUserProfile(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(!string.IsNullOrEmpty(result.Content), "Content should not be empty, a genuine username was provided.");
        }


        [TestMethod]
        public void GetUserProfile_Non_Existing_User_test()
        {
            //Assemble
            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");

            //Act
            GetUserProfileServiceViewModel viewModel = new GetUserProfileServiceViewModel();
            viewModel.ClientCode = "ClientA"; //genuine client
            viewModel.UserName = "bogus"; //genuine username
            viewModel.Email = "user"; //no email
            viewModel.Timestamp = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            viewModel.Signature = TestHelper.GetSignature(viewModel.ClientCode + "|" + viewModel.Timestamp, "ClientAPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.GetUserProfile(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(string.IsNullOrEmpty(result.Content) || result.Content.StartsWith("<serviceException"), "Content should be empty, user does not exist.");
        }


        [TestMethod]
        public void GetUserProfile_IsSessionActive_InvalidSession_Test()
        {
            //Assemble
            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");

            //Act
            IsSessionActiveViewModel viewModel = new IsSessionActiveViewModel();
            viewModel.ClientCode = "ClientA"; //genuine client
            viewModel.SignOnSessionId = "bogus"; //genuine username
            viewModel.Timestamp = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            viewModel.Signature = TestHelper.GetSignature(viewModel.SignatureContent, "ClientAPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.GetSessionProfile(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(string.IsNullOrEmpty(result.Content) || result.Content.StartsWith("<serviceException"), "Content should be empty, session does not exist.");
        }



        [TestMethod]
        public void GetUserProfile_IsSessionActive_Valid_Session_Test()
        {
            //Assemble
            
            IUserAccount userAccount = TestHelper.CreateVerifiedDummyUser("username", "password");
            Guid userSessionId = UserSessionHelper.StartUserSession(userAccount.Id, "127.0.0.1", false);
            string clientSessionId = Guid.NewGuid().ToString();
            string clientSignoutUrl = "http://somewhere.sherwood.codeplex.com/signout/?ticket={signoutticket}&signature={signoutsignature}";
            Data.UserSessions.AddUserSessionClientRecord(userSessionId, clientSessionId, clientSignoutUrl);


            //Act
            IsSessionActiveViewModel viewModel = new IsSessionActiveViewModel();
            viewModel.ClientCode = "ClientA"; //genuine client
            viewModel.SignOnSessionId = userSessionId.ToString(); //genuine session
            viewModel.Timestamp = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            viewModel.Signature = TestHelper.GetSignature(viewModel.SignatureContent, "ClientAPrivateKey");

            ServicesController controller = new ServicesController();
            ContentResult result = controller.GetSessionProfile(viewModel) as ContentResult;
            UserSessionHelper.EndUserSession(userSessionId); //simulate ending session
            ContentResult result2 = controller.GetSessionProfile(viewModel) as ContentResult;

            //Assert
            Assert.IsTrue(!string.IsNullOrEmpty(result.Content) || result.Content.StartsWith("<serviceException"), "Content should not be empty, session should exist.");
            Assert.IsTrue(string.IsNullOrEmpty(result2.Content) || result2.Content.StartsWith("<serviceException"), "Content should be empty, session should be ended.");
        }


    }
}
